package com.edroid.droidhelper.pm;

import java.io.BufferedInputStream;
import java.io.DataInputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.InputStream;
import java.security.MessageDigest;

import com.edroid.droidhelper.pm.bean.PluginVerifyResults;
import com.edroid.droidhelper.pm.utils.Logger;


/**
 * 插件完整性校验模块
 * 
 * @author terry
 *
 */
public class PluginVerifyer {
	static final Logger log = Logger.create(PluginVerifyer.class.getSimpleName());
	
	// 插件生成器的版本
	private final static int MAKER_VER = 2;
	private final static int MAKER_VER_LEN = 4;
	private final static int SECURITY_LEN = 128;
	private final static long MD5_KEY = 0x20131227;
	
	
	public static boolean verify(String path) {
		return verify(new File(path));
	}
	
	public static boolean verify(File file) {
		return verifyEx(file) != null;
	}
	
	public static PluginVerifyResults verifyEx(File file) {
		InputStream is = null;
		try {
			log.i("start verify:" + file.getAbsolutePath());
			is = new FileInputStream(file);
			PluginVerifyResults ret = verify(is);
			return ret;
		} catch (Exception e) {
			log.e("verify fail, " + e.getMessage());
		} finally {
			try {
				is.close();
			} catch (Exception e2) {
			}
		}
		
		return null;
	}
	
	private static PluginVerifyResults verify(InputStream is) throws Exception {
		DataInputStream data = null;
		try{
			data = new DataInputStream(new BufferedInputStream(is, 32));
			
			long len = data.available();
			// 标记最初位置
			data.mark((int) len+1);
			// 指向末尾 MAKER_VER_LEN 个字节
			data.skipBytes((int) (len - MAKER_VER_LEN));
			// 指向末尾
			byte[] maker_ver_buff = new byte[MAKER_VER_LEN];
			data.read(maker_ver_buff);
			long maker_ver = get32(maker_ver_buff, 0);
			if (maker_ver != MAKER_VER) { //校验加密版本
				throw new Exception("EncryptVersionNotSupportException");
			}
			
			// 指向末尾 MAKER_VER_LEN + 22 个字节
			data.reset();
			data.skipBytes((int) (len - MAKER_VER_LEN - 22));
			
			// 指向末尾 MAKER_VER_LEN 个字节
			byte[] endBuff = new byte[22];
			data.read(endBuff);
			long cenOffset = get32(endBuff, 16);
			long security_offset = cenOffset - SECURITY_LEN + 16;
			// 指向 security_offset 的位置
			data.reset();
			data.skipBytes((int) security_offset);
			long security_data_len = len - security_offset;
			
			// 指向末尾
			byte[] security_data_buff = new byte[(int) security_data_len];
			data.read(security_data_buff);
			
			// MD5校验
			byte[] md5KeyBuff = new byte[4];
			set32(md5KeyBuff, 0, MD5_KEY);
			MessageDigest md5 = MessageDigest.getInstance("MD5");
			md5.update(security_data_buff);
			md5.update(md5KeyBuff);
			
			byte[] digest = md5.digest();
			String md5String = bytesToHexString(digest, 0, digest.length);
			md5 = MessageDigest.getInstance("MD5");
			md5.update(md5String.getBytes());
			digest = md5.digest();
			// 指向 cenOffset - SECURITY_LEN
			data.reset();
			data.skipBytes((int) (cenOffset - SECURITY_LEN));
			// 指向 cenOffset - SECURITY_LEN + 16
			byte[] oldDigest = new byte[16];
			data.read(oldDigest);
			for (int i = 0; i < digest.length; i++) {
				if (digest[i] != oldDigest[i]) {
					throw new SecurityException("IllegalJarFile!");
				}
			}
			
			// 指向cenOffset - SECURITY_LEN + 20
			byte[] verBuff = new byte[4];
			data.read(verBuff);

			// 指向cenOffset - SECURITY_LEN + 22
			byte[] nameLenBuff = new byte[2];
			data.read(nameLenBuff);

			long pluginVerNumber = get32(verBuff, 0);
			int pluginNameLen = get16(nameLenBuff, 0);
			
			byte[] nameBuff = new byte[pluginNameLen];
			data.read(nameBuff);

			PluginVerifyResults ret = new PluginVerifyResults();
			ret.name = new String(nameBuff);
			ret.ver = (int) pluginVerNumber;
			
			log.i("verify pass!");

			return ret;
		} catch (Exception e) {
			throw e;
		} finally {
			try {
				data.close();
			} catch (Exception e2) {
			}
		}
	}
	
	private static int get16(byte b[], int off) {
		return (b[off] & 0xff) | ((b[off + 1] & 0xff) << 8);
	}

	private static long get32(byte b[], int off) {
		return get16(b, off) | ((long) get16(b, off + 2) << 16);
	}

	private static void set32(byte[] b, int offset, long value) {
		b[offset] = (byte) (value & 0xFF);
		b[offset + 1] = (byte) ((value >> 8) & 0xFF);
		b[offset + 2] = (byte) ((value >> 16) & 0xFF);
		b[offset + 3] = (byte) ((value >> 24) & 0xFF);
	}
	
	private static String bytesToHexString(byte[] data, int offset, int len) {
		char[] str = new char[len*2];
		
		final int END = offset + len;
		for (int j=0, i = offset; i < END; i++) {
			// 将没个数(int)b进行双字节加密
			byte b = data[i];
			
			str[j++] = hexDigitsChr[b >> 4 & 0xf];
			str[j++] = hexDigitsChr[b & 0xf];
		}
		
		return new String(str);
	}
	
	// 十六进制下数字到字符的映射数组
	private final static char[] hexDigitsChr = {
		'0', '1', '2', 
		'3', '4', '5',
		'6', '7', '8', 
		'9', 'a', 'b',
		'c', 'd', 'e',
		'f' 
		};
}
